In [1]:
import pandas as pd
import numpy as np
from scipy.stats import chi2_contingency
import plotly.express as px
import plotly.graph_objects as go
import plotly.subplots as sp
import scipy.stats as stats
import scikit_posthocs as sph
import seaborn as sns
import matplotlib as plt
In [2]:
df_merge = pd.read_csv("df_merge.csv")
df_age_categ = pd.read_csv("df_age_categ.csv")
df_nb_achat_categ = pd.read_csv("df_nb_achat_categ.csv")
In [3]:
df_age_categ.head()
Out[3]:
Unnamed: 0 id_prod price categ date session_id client_id sex birth month age
0 0 0_1421 19.99 0 2021-03-01 04:13:00.107748 s_101 c_8533 m 1972 3 51
1 1 0_1421 19.99 0 2022-10-01 04:13:00.107748 s_276043 c_8533 m 1972 10 51
2 2 0_1421 19.99 0 2022-12-01 04:13:00.107748 s_305391 c_8533 m 1972 12 51
3 3 0_1421 19.99 0 2023-01-01 04:13:00.107748 s_320253 c_8533 m 1972 1 51
4 4 0_2199 12.99 0 2021-03-25 17:43:48.819074 s_11366 c_8533 m 1972 3 51
In [4]:
df_merge.head()
Out[4]:
Unnamed: 0 client_id age sex panier_total nb_achat premier_mois dernier_mois nb_mois frequence_achat item_par_panier
0 0 c_1 68 m 629.02 43 24258 24277 20 2.150000 1.264706
1 1 c_10 67 m 1353.60 58 24255 24277 23 2.521739 1.705882
2 2 c_100 31 m 254.85 8 24256 24273 18 0.444444 1.600000
3 3 c_1000 57 f 2291.88 126 24255 24277 23 5.478261 1.340426
4 4 c_1001 41 m 1823.85 103 24255 24278 24 4.291667 2.191489
In [5]:
df_merge = df_merge.drop(columns='Unnamed: 0')
In [6]:
bins = [18, 25, 40, 50, 65, 100]
labels = ["Jeunes adultes", "Jeunes actifs", "Familles & Pros", "Seniors actifs", "Retraités"]

df_merge["segment_age"] = pd.cut(df_merge["age"], bins=bins, labels=labels)

print(df_merge[["age", "segment_age"]].head())
   age      segment_age
0   68        Retraités
1   67        Retraités
2   31    Jeunes actifs
3   57   Seniors actifs
4   41  Familles & Pros
In [7]:
groupe_1 = df_merge[df_merge["segment_age"] == "Jeunes adultes"]
groupe_2 = df_merge[df_merge["segment_age"] == "Jeunes actifs"]
groupe_3 = df_merge[df_merge["segment_age"] == "Familles & Pros"]
groupe_4 = df_merge[df_merge["segment_age"] == "Seniors actifs"]
groupe_5 = df_merge[df_merge["segment_age"] == "Retraités"]
In [8]:
bins = [18, 25, 40, 50, 65, 100]
labels = ["Jeunes adultes", "Jeunes actifs", "Familles & Pros", "Seniors actifs", "Retraités"]

df_age_categ["segment_age"] = pd.cut(df_age_categ["age"], bins=bins, labels=labels)

print(df_age_categ[["age", "segment_age"]].head())

groupe_1_khi = df_age_categ[df_age_categ["segment_age"] == "Jeunes adultes"]
groupe_2_khi = df_age_categ[df_age_categ["segment_age"] == "Jeunes actifs"]
groupe_3_khi = df_age_categ[df_age_categ["segment_age"] == "Familles & Pros"]
groupe_4_khi = df_age_categ[df_age_categ["segment_age"] == "Seniors actifs"]
groupe_5_khi = df_age_categ[df_age_categ["segment_age"] == "Retraités"]
   age     segment_age
0   51  Seniors actifs
1   51  Seniors actifs
2   51  Seniors actifs
3   51  Seniors actifs
4   51  Seniors actifs
In [9]:
df_merge.head()
Out[9]:
client_id age sex panier_total nb_achat premier_mois dernier_mois nb_mois frequence_achat item_par_panier segment_age
0 c_1 68 m 629.02 43 24258 24277 20 2.150000 1.264706 Retraités
1 c_10 67 m 1353.60 58 24255 24277 23 2.521739 1.705882 Retraités
2 c_100 31 m 254.85 8 24256 24273 18 0.444444 1.600000 Jeunes actifs
3 c_1000 57 f 2291.88 126 24255 24277 23 5.478261 1.340426 Seniors actifs
4 c_1001 41 m 1823.85 103 24255 24278 24 4.291667 2.191489 Familles & Pros
In [10]:
df_age_categ.head()
Out[10]:
Unnamed: 0 id_prod price categ date session_id client_id sex birth month age segment_age
0 0 0_1421 19.99 0 2021-03-01 04:13:00.107748 s_101 c_8533 m 1972 3 51 Seniors actifs
1 1 0_1421 19.99 0 2022-10-01 04:13:00.107748 s_276043 c_8533 m 1972 10 51 Seniors actifs
2 2 0_1421 19.99 0 2022-12-01 04:13:00.107748 s_305391 c_8533 m 1972 12 51 Seniors actifs
3 3 0_1421 19.99 0 2023-01-01 04:13:00.107748 s_320253 c_8533 m 1972 1 51 Seniors actifs
4 4 0_2199 12.99 0 2021-03-25 17:43:48.819074 s_11366 c_8533 m 1972 3 51 Seniors actifs
In [ ]:
 
In [11]:
def interpret_anderson(result):
    for i in range(len(result.critical_values)):
        sig_level = result.significance_level[i]
        if result.statistic > result.critical_values[i]:
            print(f"Au seuil de {sig_level}%, les données NE SUIVENT PAS une distribution normale.")
        else:
            print(f"Au seuil de {sig_level}%, les données SUIVENT une distribution normale.")
In [12]:
df_homme = df_merge[df_merge["sex"] == "m"]
df_femme = df_merge[df_merge["sex"] == "f"]
In [13]:
df_homme.head()
Out[13]:
client_id age sex panier_total nb_achat premier_mois dernier_mois nb_mois frequence_achat item_par_panier segment_age
0 c_1 68 m 629.02 43 24258 24277 20 2.150000 1.264706 Retraités
1 c_10 67 m 1353.60 58 24255 24277 23 2.521739 1.705882 Retraités
2 c_100 31 m 254.85 8 24256 24273 18 0.444444 1.600000 Jeunes actifs
4 c_1001 41 m 1823.85 103 24255 24278 24 4.291667 2.191489 Familles & Pros
6 c_1003 41 m 1230.83 106 24255 24278 24 4.416667 2.355556 Familles & Pros
In [14]:
df_counts = df_age_categ.groupby(["categ", "sex", "segment_age"]).size().unstack(fill_value=0).reset_index()
df_counts_2 = df_age_categ.groupby(["categ", "sex"]).size().unstack(fill_value=0).reset_index()
df_counts.head()
Out[14]:
segment_age categ sex Jeunes adultes Jeunes actifs Familles & Pros Seniors actifs Retraités
0 0 f 4990 71272 81206 28542 14783
1 0 m 4604 69736 72251 27567 12330
2 1 f 8743 28490 26199 32077 20212
3 1 m 8388 27018 23644 29849 15985
4 2 f 8647 7106 427 506 294
In [15]:
print(df_counts.columns)
Index(['categ', 'sex', 'Jeunes adultes', 'Jeunes actifs', 'Familles & Pros',
       'Seniors actifs', 'Retraités'],
      dtype='object', name='segment_age')
In [16]:
df_counts = df_counts.set_index(["categ", "sex"])
In [17]:
cols_effectifs = ["Jeunes adultes", "Jeunes actifs", "Familles & Pros", "Seniors actifs", "Retraités"]

table_contingence = df_counts[cols_effectifs]

chi2, p_value, dof, expected = chi2_contingency(table_contingence)

print("Chi² =", chi2)
print("p-value =", p_value)
print("Degrés de liberté =", dof)

n = table_contingence.to_numpy().sum()
min_dim = min(table_contingence.shape)

v_cramer = np.sqrt((chi2 / n) / min_dim)
print("V de Cramer =", v_cramer)
Chi² = 170895.3041612725
p-value = 0.0
Degrés de liberté = 20
V de Cramer = 0.23096234347711397
In [18]:
print(df_counts.columns)
Index(['Jeunes adultes', 'Jeunes actifs', 'Familles & Pros', 'Seniors actifs',
       'Retraités'],
      dtype='object', name='segment_age')
In [19]:
colonnes_a_comparer = ["f", "m"]
df_subset = df_counts_2[colonnes_a_comparer]
df_long = df_counts_2.melt(id_vars="categ", var_name="Sexe", value_name="Effectif")

tableau_contingence = pd.crosstab(df_long["categ"], df_long["Sexe"], values=df_long["Effectif"], aggfunc="sum")
In [20]:
df_subset.head()
Out[20]:
sex f m
0 200793 186488
1 115721 104884
2 16980 15868
In [21]:
stat, p, dof, expected = stats.chi2_contingency(tableau_contingence)

N = tableau_contingence.sum().sum()
cramer_v = np.sqrt(stat / (N * min(2, 2)))

print(f"Statistique Khi-2 : {stat}")
print(f"P-valeur : {p:.2f}")
print(f"V de Cramer : {cramer_v:.4f}")
print(f"Degrés de liberté : {dof}")
df_khi = pd.DataFrame(expected)

alpha = 0.05
if p < alpha:
    print("Il y a une dépendance significative entre les catégories et le sexe (Rejet de H0).")
else:
    print("Aucune dépendance significative (On ne rejette pas H0).")
Statistique Khi-2 : 22.66856665178056
P-valeur : 0.00
V de Cramer : 0.0042
Degrés de liberté : 2
Il y a une dépendance significative entre les catégories et le sexe (Rejet de H0).
In [22]:
df_khi.index.name = "categ"
In [ ]:
 
In [23]:
df_khi = df_khi.astype(int)
In [24]:
df_merge_khi = pd.merge(df_counts, df_khi, how="inner", on='categ')
df_merge_khi.head()
Out[24]:
Jeunes adultes Jeunes actifs Familles & Pros Seniors actifs Retraités 0 1
categ
0 4990 71272 81206 28542 14783 201574 185706
0 4604 69736 72251 27567 12330 201574 185706
1 8743 28490 26199 32077 20212 114822 105782
1 8388 27018 23644 29849 15985 114822 105782
2 8647 7106 427 506 294 17096 15751
In [25]:
df_merge_khi = df_merge_khi.rename(columns={"f":"fo", "m":"mo", 0:"fe", 1:"me"})
In [26]:
df_merge_khi.head()
Out[26]:
Jeunes adultes Jeunes actifs Familles & Pros Seniors actifs Retraités fe me
categ
0 4990 71272 81206 28542 14783 201574 185706
0 4604 69736 72251 27567 12330 201574 185706
1 8743 28490 26199 32077 20212 114822 105782
1 8388 27018 23644 29849 15985 114822 105782
2 8647 7106 427 506 294 17096 15751


Lien entre l'âge et le montant total des achats


In [28]:
df_merge.head()
Out[28]:
client_id age sex panier_total nb_achat premier_mois dernier_mois nb_mois frequence_achat item_par_panier segment_age
0 c_1 68 m 629.02 43 24258 24277 20 2.150000 1.264706 Retraités
1 c_10 67 m 1353.60 58 24255 24277 23 2.521739 1.705882 Retraités
2 c_100 31 m 254.85 8 24256 24273 18 0.444444 1.600000 Jeunes actifs
3 c_1000 57 f 2291.88 126 24255 24277 23 5.478261 1.340426 Seniors actifs
4 c_1001 41 m 1823.85 103 24255 24278 24 4.291667 2.191489 Familles & Pros
In [29]:
bins = [18, 25, 40, 50, 65, 100]
labels = ["Jeunes adultes", "Jeunes actifs", "Familles & Pros", "Seniors actifs", "Retraités"]

df_merge["segment_age"] = pd.cut(df_merge["age"], bins=bins, labels=labels)

print(df_merge[["age", "segment_age"]].head())
   age      segment_age
0   68        Retraités
1   67        Retraités
2   31    Jeunes actifs
3   57   Seniors actifs
4   41  Familles & Pros
In [30]:
df_merge.head()
Out[30]:
client_id age sex panier_total nb_achat premier_mois dernier_mois nb_mois frequence_achat item_par_panier segment_age
0 c_1 68 m 629.02 43 24258 24277 20 2.150000 1.264706 Retraités
1 c_10 67 m 1353.60 58 24255 24277 23 2.521739 1.705882 Retraités
2 c_100 31 m 254.85 8 24256 24273 18 0.444444 1.600000 Jeunes actifs
3 c_1000 57 f 2291.88 126 24255 24277 23 5.478261 1.340426 Seniors actifs
4 c_1001 41 m 1823.85 103 24255 24278 24 4.291667 2.191489 Familles & Pros
In [31]:
groupe_1 = df_merge[df_merge["segment_age"] == "Jeunes adultes"]["panier_total"]
groupe_2 = df_merge[df_merge["segment_age"] == "Jeunes actifs"]["panier_total"]
groupe_3 = df_merge[df_merge["segment_age"] == "Familles & Pros"]["panier_total"]
groupe_4 = df_merge[df_merge["segment_age"] == "Seniors actifs"]["panier_total"]
groupe_5 = df_merge[df_merge["segment_age"] == "Retraités"]["panier_total"]
In [32]:
l_stat, p_value = stats.levene(groupe_1, groupe_2,  groupe_3, groupe_4, groupe_5)

print(f"Test de levene: {l_stat:.3f}")
print(f"Valeur p: {p_value:.5f}")
Test de levene: 92.458
Valeur p: 0.00000
In [33]:
fig = px.scatter(df_merge, x="age", y="panier_total", 
                 title="Relation entre l'âge et le montant dépensé au total",
                 labels={"age": "Âge du client", "montant_panier": "Montant du panier (€)"},
                 trendline="lowess")

fig.show()
In [34]:
groupes = {
    "Jeunes adultes": groupe_1,
    "Jeunes actifs": groupe_2,
    "Familles & Pros": groupe_3,
    "Seniors actifs": groupe_4,
    "Retraités": groupe_5
}

for nom, groupe in groupes.items():
    result = stats.anderson(groupe, dist='norm')
    print(f"\nTest d'Anderson-Darling pour {nom}")
    interpret_anderson(result)
Test d'Anderson-Darling pour Jeunes adultes
Au seuil de 15.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 10.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 5.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 2.5%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 1.0%, les données NE SUIVENT PAS une distribution normale.

Test d'Anderson-Darling pour Jeunes actifs
Au seuil de 15.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 10.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 5.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 2.5%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 1.0%, les données NE SUIVENT PAS une distribution normale.

Test d'Anderson-Darling pour Familles & Pros
Au seuil de 15.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 10.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 5.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 2.5%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 1.0%, les données NE SUIVENT PAS une distribution normale.

Test d'Anderson-Darling pour Seniors actifs
Au seuil de 15.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 10.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 5.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 2.5%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 1.0%, les données NE SUIVENT PAS une distribution normale.

Test d'Anderson-Darling pour Retraités
Au seuil de 15.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 10.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 5.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 2.5%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 1.0%, les données NE SUIVENT PAS une distribution normale.
In [35]:
anderson_age = stats.anderson(df_merge["age"], dist='norm')
print("Test d'Anderson-Darling pour Age")
print(f"Statistique : {anderson_age.statistic:.4f}")
print(f"Valeurs critiques : {anderson_age.critical_values}")
print(f"Niveaux de signification : {anderson_age.significance_level}")

print("\nInterprétation pour Age :")
interpret_anderson(anderson_age)
Test d'Anderson-Darling pour Age
Statistique : 53.7635
Valeurs critiques : [0.576 0.656 0.787 0.918 1.091]
Niveaux de signification : [15.  10.   5.   2.5  1. ]

Interprétation pour Age :
Au seuil de 15.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 10.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 5.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 2.5%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 1.0%, les données NE SUIVENT PAS une distribution normale.
In [36]:
stat, p_value = stats.kruskal(groupe_1, groupe_2, groupe_3, groupe_4, groupe_5)

print(f" Kruskal-Wallis : Statistique H = {stat:.4f}")
print(f" p-value = {p_value:.5f}")

if p_value < 0.05:
    print("Il y a une différence significative entre au moins un des groupes.")
else:
    print("Pas de différence significative entre les groupes.")
 Kruskal-Wallis : Statistique H = 537.4040
 p-value = 0.00000
Il y a une différence significative entre au moins un des groupes.
In [37]:
data = [groupe_1, groupe_2, groupe_3, groupe_4, groupe_5]

dunn_results = sph.posthoc_dunn(data, p_adjust="bonferroni")

dunn_df = pd.DataFrame(dunn_results.values, index=labels, columns=labels)

fig = px.imshow(
    dunn_df,
    labels=dict(x="Groupe 1", y="Groupe 2", color="p-value"),
    x=labels,
    y=labels,
    color_continuous_scale="RdBu_r",
    title="Test post-hoc de Dunn (p-values)"
)

fig.show()
In [38]:
df_test = df_merge.groupby(["segment_age"])
for i, o in df_test:
    correlation, p_value = stats.spearmanr(o['age'], o['panier_total'])
    print(f"{i}, {correlation:.5f}")
    print(f"{i}, {p_value:.5f}")
    print()
('Jeunes adultes',), -0.00839
('Jeunes adultes',), 0.76402

('Jeunes actifs',), 0.11571
('Jeunes actifs',), 0.00000

('Familles & Pros',), 0.01071
('Familles & Pros',), 0.65309

('Seniors actifs',), -0.07534
('Seniors actifs',), 0.00089

('Retraités',), -0.03969
('Retraités',), 0.17583

In [39]:
"""
Les variables ne suivent pas une variance équivalente entre elles 
La distribution n'est pas normale
Le panier moyen décroit avec l'age qui avance
La relation entre les deux variable est très significative mais pas énorme

"""
Out[39]:
"\nLes variables ne suivent pas une variance équivalente entre elles \nLa distribution n'est pas normale\nLe panier moyen décroit avec l'age qui avance\nLa relation entre les deux variable est très significative mais pas énorme\n\n"


Lien entre l'âge et la fréquence d'achat


In [41]:
df_merge.head()
Out[41]:
client_id age sex panier_total nb_achat premier_mois dernier_mois nb_mois frequence_achat item_par_panier segment_age
0 c_1 68 m 629.02 43 24258 24277 20 2.150000 1.264706 Retraités
1 c_10 67 m 1353.60 58 24255 24277 23 2.521739 1.705882 Retraités
2 c_100 31 m 254.85 8 24256 24273 18 0.444444 1.600000 Jeunes actifs
3 c_1000 57 f 2291.88 126 24255 24277 23 5.478261 1.340426 Seniors actifs
4 c_1001 41 m 1823.85 103 24255 24278 24 4.291667 2.191489 Familles & Pros
In [42]:
bins = [18, 25, 40, 50, 65, 100]
labels = ["Jeunes adultes", "Jeunes actifs", "Familles & Pros", "Seniors actifs", "Retraités"]

df_merge["segment_age"] = pd.cut(df_merge["age"], bins=bins, labels=labels)

print(df_merge[["age", "segment_age"]].head())
   age      segment_age
0   68        Retraités
1   67        Retraités
2   31    Jeunes actifs
3   57   Seniors actifs
4   41  Familles & Pros
In [43]:
groupe_1 = df_merge[df_merge["segment_age"] == "Jeunes adultes"]["frequence_achat"]
groupe_2 = df_merge[df_merge["segment_age"] == "Jeunes actifs"]["frequence_achat"]
groupe_3 = df_merge[df_merge["segment_age"] == "Familles & Pros"]["frequence_achat"]
groupe_4 = df_merge[df_merge["segment_age"] == "Seniors actifs"]["frequence_achat"]
groupe_5 = df_merge[df_merge["segment_age"] == "Retraités"]["frequence_achat"]
In [44]:
groupes = {
    "Jeunes adultes": groupe_1,
    "Jeunes actifs": groupe_2,
    "Familles & Pros": groupe_3,
    "Seniors actifs": groupe_4,
    "Retraités": groupe_5
}

for nom, groupe in groupes.items():
    result = stats.anderson(groupe, dist='norm')
    print(f"\nTest d'Anderson-Darling pour {nom}")
    interpret_anderson(result)
Test d'Anderson-Darling pour Jeunes adultes
Au seuil de 15.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 10.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 5.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 2.5%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 1.0%, les données NE SUIVENT PAS une distribution normale.

Test d'Anderson-Darling pour Jeunes actifs
Au seuil de 15.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 10.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 5.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 2.5%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 1.0%, les données NE SUIVENT PAS une distribution normale.

Test d'Anderson-Darling pour Familles & Pros
Au seuil de 15.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 10.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 5.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 2.5%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 1.0%, les données NE SUIVENT PAS une distribution normale.

Test d'Anderson-Darling pour Seniors actifs
Au seuil de 15.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 10.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 5.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 2.5%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 1.0%, les données NE SUIVENT PAS une distribution normale.

Test d'Anderson-Darling pour Retraités
Au seuil de 15.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 10.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 5.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 2.5%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 1.0%, les données NE SUIVENT PAS une distribution normale.
In [45]:
anderson_panier = stats.anderson(df_merge["frequence_achat"], dist='norm')
print("\nTest d'Anderson-Darling pour Panier Total")
print(f"Statistique : {anderson_panier.statistic:.4f}")
print(f"Valeurs critiques : {anderson_panier.critical_values}")
print(f"Niveaux de signification : {anderson_panier.significance_level}")

def interpret_anderson(result):
    for i in range(len(result.critical_values)):
        sig_level = result.significance_level[i]
        if result.statistic > result.critical_values[i]:
            print(f"Au seuil de {sig_level}%, les données NE SUIVENT PAS une distribution normale.")
        else:
            print(f"Au seuil de {sig_level}%, les données SUIVENT une distribution normale.")

print("\nInterprétation pour Age :")
interpret_anderson(anderson_age)

print("\nInterprétation pour Panier Total :")
interpret_anderson(anderson_panier)
Test d'Anderson-Darling pour Panier Total
Statistique : 479.4178
Valeurs critiques : [0.576 0.656 0.787 0.918 1.091]
Niveaux de signification : [15.  10.   5.   2.5  1. ]

Interprétation pour Age :
Au seuil de 15.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 10.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 5.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 2.5%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 1.0%, les données NE SUIVENT PAS une distribution normale.

Interprétation pour Panier Total :
Au seuil de 15.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 10.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 5.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 2.5%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 1.0%, les données NE SUIVENT PAS une distribution normale.
In [46]:
k_stats, p_value = stats.kruskal(groupe_1, groupe_2, groupe_3, groupe_4, groupe_5)
print(f"{k_stats:.5f}")
print(f"{p_value:.5f}")
1388.89986
0.00000
In [47]:
data = [groupe_1, groupe_2, groupe_3, groupe_4, groupe_5]


dunn_results = sph.posthoc_dunn(data, p_adjust="bonferroni")

dunn_df = pd.DataFrame(dunn_results.values, index=labels, columns=labels)

fig = px.imshow(
    dunn_df,
    labels=dict(x="Groupe 1", y="Groupe 2", color="p-value"),
    x=labels,
    y=labels,
    color_continuous_scale="RdBu_r",
    title="Test post-hoc de Dunn (p-values)"
)

fig.show()
In [48]:
fig = px.scatter(df_merge, x="age", y="frequence_achat", 
                 title="Relation entre l'âge et la fréquence d'achat",
                 labels={"age": "Âge du client", "frequence_achat": "Nombre d'achat par mois"},
                 trendline="lowess")

fig.show()
In [49]:
df_test = df_merge.groupby(["segment_age"])
for i, o in df_test:
    correlation, p_value = stats.spearmanr(o['age'], o['frequence_achat'])
    print(f"{i}, {correlation:.5f}")
    print(f"{i}, {p_value:.5f}")
    print()
('Jeunes adultes',), -0.01266
('Jeunes adultes',), 0.65058

('Jeunes actifs',), 0.55830
('Jeunes actifs',), 0.00000

('Familles & Pros',), 0.01666
('Familles & Pros',), 0.48415

('Seniors actifs',), -0.10346
('Seniors actifs',), 0.00000

('Retraités',), -0.03891
('Retraités',), 0.18449

In [50]:
"""
Il y a des différences significative entre les groupes d'âges
La fréquence d'achat corrèle positivement avec l'âge
On peut en déduire que chaque générations a ses habitudes ?
"""
Out[50]:
"\nIl y a des différences significative entre les groupes d'âges\nLa fréquence d'achat corrèle positivement avec l'âge\nOn peut en déduire que chaque générations a ses habitudes ?\n"
In [51]:
print(df_merge["frequence_achat"].describe())
count    8596.000000
mean        3.206055
std         2.796067
min         0.111111
25%         1.304348
50%         2.291667
75%         4.125000
max        16.875000
Name: frequence_achat, dtype: float64
In [52]:
hommes = df_merge[df_merge["sex"] == "m"]["frequence_achat"]
femmes = df_merge[df_merge["sex"] == "f"]["frequence_achat"]


Lien entre l'âge et le panier moyen


In [54]:
df_merge.head()
Out[54]:
client_id age sex panier_total nb_achat premier_mois dernier_mois nb_mois frequence_achat item_par_panier segment_age
0 c_1 68 m 629.02 43 24258 24277 20 2.150000 1.264706 Retraités
1 c_10 67 m 1353.60 58 24255 24277 23 2.521739 1.705882 Retraités
2 c_100 31 m 254.85 8 24256 24273 18 0.444444 1.600000 Jeunes actifs
3 c_1000 57 f 2291.88 126 24255 24277 23 5.478261 1.340426 Seniors actifs
4 c_1001 41 m 1823.85 103 24255 24278 24 4.291667 2.191489 Familles & Pros
In [55]:
groupe_1 = df_merge[df_merge["segment_age"] == "Jeunes adultes"]["item_par_panier"]
groupe_2 = df_merge[df_merge["segment_age"] == "Jeunes actifs"]["item_par_panier"]
groupe_3 = df_merge[df_merge["segment_age"] == "Familles & Pros"]["item_par_panier"]
groupe_4 = df_merge[df_merge["segment_age"] == "Seniors actifs"]["item_par_panier"]
groupe_5 = df_merge[df_merge["segment_age"] == "Retraités"]["item_par_panier"]
In [56]:
groupes = {
    "Jeunes adultes": groupe_1,
    "Jeunes actifs": groupe_2,
    "Familles & Pros": groupe_3,
    "Seniors actifs": groupe_4,
    "Retraités": groupe_5
}

for nom, groupe in groupes.items():
    result = stats.anderson(groupe, dist='norm')
    print(f"\nTest d'Anderson-Darling pour {nom}")
    interpret_anderson(result)
Test d'Anderson-Darling pour Jeunes adultes
Au seuil de 15.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 10.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 5.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 2.5%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 1.0%, les données NE SUIVENT PAS une distribution normale.

Test d'Anderson-Darling pour Jeunes actifs
Au seuil de 15.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 10.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 5.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 2.5%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 1.0%, les données NE SUIVENT PAS une distribution normale.

Test d'Anderson-Darling pour Familles & Pros
Au seuil de 15.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 10.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 5.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 2.5%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 1.0%, les données NE SUIVENT PAS une distribution normale.

Test d'Anderson-Darling pour Seniors actifs
Au seuil de 15.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 10.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 5.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 2.5%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 1.0%, les données NE SUIVENT PAS une distribution normale.

Test d'Anderson-Darling pour Retraités
Au seuil de 15.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 10.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 5.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 2.5%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 1.0%, les données NE SUIVENT PAS une distribution normale.
In [57]:
fig = px.scatter(df_merge, x="age", y="item_par_panier", 
                 title="Relation entre l'âge et le nombre de produit par panier",
                 labels={"age": "Âge du client", "item_par_panier": "Nombre de produit par panier"},
                 trendline="lowess")

fig.show()
In [58]:
spearman_corr, p_value = stats.spearmanr(df_merge["age"], df_merge["item_par_panier"])

print(f"Corrélation de Spearman: {spearman_corr:.3f}")
print(f"Valeur p: {p_value:.5f}")
Corrélation de Spearman: -0.208
Valeur p: 0.00000
In [59]:
df_test = df_merge.groupby(["segment_age"])
for i, o in df_test:
    correlation, p_value = stats.spearmanr(o['age'], o['item_par_panier'])
    print(f"{i}, {correlation:.5f}")
    print(f"{i}, {p_value:.5f}")
    print()
('Jeunes adultes',), 0.00842
('Jeunes adultes',), 0.76320

('Jeunes actifs',), 0.45837
('Jeunes actifs',), 0.00000

('Familles & Pros',), 0.02529
('Familles & Pros',), 0.28833

('Seniors actifs',), -0.15349
('Seniors actifs',), 0.00000

('Retraités',), 0.01323
('Retraités',), 0.65200

In [60]:
anderson_item_par_panier = stats.anderson(df_merge["item_par_panier"], dist='norm')
print(f"Statistique : {anderson_item_par_panier.statistic:.4f}")
print(f"Valeurs critiques : {anderson_item_par_panier.critical_values}")
print(f"Niveaux de signification : {anderson_item_par_panier.significance_level}")


print("\nInterprétation pour Age :")
interpret_anderson(anderson_age)

print("\nInterprétation pour Age :")
interpret_anderson(anderson_item_par_panier)
Statistique : 77.9237
Valeurs critiques : [0.576 0.656 0.787 0.918 1.091]
Niveaux de signification : [15.  10.   5.   2.5  1. ]

Interprétation pour Age :
Au seuil de 15.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 10.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 5.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 2.5%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 1.0%, les données NE SUIVENT PAS une distribution normale.

Interprétation pour Age :
Au seuil de 15.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 10.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 5.0%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 2.5%, les données NE SUIVENT PAS une distribution normale.
Au seuil de 1.0%, les données NE SUIVENT PAS une distribution normale.
In [61]:
m_stat, p_value = stats.kruskal(groupe_1, groupe_2, groupe_3, groupe_4, groupe_5)
print(f"{m_stat:.5f}")
print(f"{p_value:.5f}")
2777.34075
0.00000
In [62]:
data = [groupe_1, groupe_2, groupe_3, groupe_4, groupe_5]


dunn_results = sph.posthoc_dunn(data, p_adjust="bonferroni")

dunn_df = pd.DataFrame(dunn_results.values, index=labels, columns=labels)

fig = px.imshow(
    dunn_df,
    labels=dict(x="Groupe 1", y="Groupe 2", color="p-value"),
    x=labels,
    y=labels,
    color_continuous_scale="RdBu_r",
    title="Test post-hoc de Dunn (p-values)"
)

fig.show()


Lien entre l'âge et la catégorie acheté


In [64]:
df_nb_achat_categ = df_nb_achat_categ.drop(columns="Unnamed: 0")
In [65]:
df_nb_achat_categ.head()
Out[65]:
client_id categ age depense_par_categ nb_achat
0 c_1 0 70 360.15 30.0
1 c_1 1 70 214.00 12.0
2 c_1 2 70 54.87 1.0
3 c_10 0 69 263.87 20.0
4 c_10 1 69 809.77 34.0
In [66]:
df_nb_achat_categ["Jitter"] = df_nb_achat_categ["categ"].astype(float) + np.random.uniform(-0.4, 0.4, len(df_nb_achat_categ))


fig = px.scatter(
    df_nb_achat_categ, 
    x="Jitter", 
    y="age", 
    size="nb_achat",
    color=df_nb_achat_categ["categ"].astype(str),
    labels={"Jitter": "Catégorie d'achat", "age": "Âge", "nb_achat": "Nombre d'achats"},
    title="Répartition des âges par catégorie d'achat (Taille = Nombre d'achats)",
    opacity=0.7
)


fig.update_layout(
    xaxis=dict(tickmode="array", tickvals=[0, 1, 2], ticktext=["Catégorie 0", "Catégorie 1", "Catégorie 2"]),
    xaxis_title="Catégorie d'achat",
    yaxis_title="Âge",
    height = 650
)

fig.show()
In [67]:
df_nb_achat_categ.head()
Out[67]:
client_id categ age depense_par_categ nb_achat Jitter
0 c_1 0 70 360.15 30.0 0.292943
1 c_1 1 70 214.00 12.0 0.964251
2 c_1 2 70 54.87 1.0 2.291610
3 c_10 0 69 263.87 20.0 0.356798
4 c_10 1 69 809.77 34.0 1.100067
In [68]:
df_nb_achat_categ["segment_age"] = pd.cut(df_nb_achat_categ["age"], bins=bins, labels=labels)
In [69]:
d_age = {}
d_categ = {}

nb_segment = df_nb_achat_categ['segment_age'].unique()
nb_categ = df_nb_achat_categ['categ'].unique()

for i in range(len(nb_segment)):
    d_age[f"groupe_{i}"] = df_nb_achat_categ.loc[df_nb_achat_categ['segment_age'] == nb_segment[i], ['categ', 'depense_par_categ', 'segment_age', 'age']]
In [70]:
for key in d_age:
    for o in range(len(nb_categ)):
        d_categ[f"{key}_{o}"] = d_age[key].loc[d_age[key]['categ'] == nb_categ[o], ['categ','depense_par_categ', 'segment_age', 'age']]
print(d_categ.keys())
dict_keys(['groupe_0_0', 'groupe_0_1', 'groupe_0_2', 'groupe_1_0', 'groupe_1_1', 'groupe_1_2', 'groupe_2_0', 'groupe_2_1', 'groupe_2_2', 'groupe_3_0', 'groupe_3_1', 'groupe_3_2', 'groupe_4_0', 'groupe_4_1', 'groupe_4_2'])
In [71]:
data = []

for i in d_categ.keys():
    data.append(i)

print(data)
['groupe_0_0', 'groupe_0_1', 'groupe_0_2', 'groupe_1_0', 'groupe_1_1', 'groupe_1_2', 'groupe_2_0', 'groupe_2_1', 'groupe_2_2', 'groupe_3_0', 'groupe_3_1', 'groupe_3_2', 'groupe_4_0', 'groupe_4_1', 'groupe_4_2']
In [72]:
d_categ['groupe_0_0'].head()
Out[72]:
categ depense_par_categ segment_age age
0 0 360.15 Retraités 70
3 0 263.87 Retraités 69
24 0 250.36 Retraités 84
30 0 138.08 Retraités 84
48 0 103.57 Retraités 68
In [73]:
for nom, df in d_categ.items():
        valeurs = pd.to_numeric(df['depense_par_categ'], errors='coerce').dropna()
        result = stats.anderson(valeurs, dist='norm')
        if result.statistic > 1.089:
            continue
        print(f"Test d'Anderson-Darling pour {nom}")
        interpret_anderson(result)
In [74]:
data = [
    pd.to_numeric(df["depense_par_categ"], errors="coerce").dropna().values 
    for df in d_categ.values()
]

k_stat, p_value = stats.kruskal(*data)

print(f"{k_stat:.5f}")
print(f"{p_value:.5f}")
11406.79242
0.00000
In [75]:
correlation, p_value = stats.spearmanr(df_nb_achat_categ['age'], df_nb_achat_categ['depense_par_categ'])
print(f"{correlation:.5f}")
print(f"{p_value:.5f}")
-0.08537
0.00000
In [76]:
df_test = df_nb_achat_categ.groupby(["segment_age", "categ"])

for i, o in df_test:
    correlation, p_value = stats.spearmanr(o['age'], o['depense_par_categ'])
    print(f"{i}, {correlation:.5f}")
    print(f"{i}, {p_value:.5f}")
    print()
('Jeunes adultes', 0), 0.01481
('Jeunes adultes', 0), 0.64201

('Jeunes adultes', 1), -0.06838
('Jeunes adultes', 1), 0.03162

('Jeunes adultes', 2), -0.03520
('Jeunes adultes', 2), 0.26897

('Jeunes actifs', 0), 0.70721
('Jeunes actifs', 0), 0.00000

('Jeunes actifs', 1), 0.36970
('Jeunes actifs', 1), 0.00000

('Jeunes actifs', 2), -0.71550
('Jeunes actifs', 2), 0.00000

('Familles & Pros', 0), -0.03779
('Familles & Pros', 0), 0.11185

('Familles & Pros', 1), -0.03260
('Familles & Pros', 1), 0.17030

('Familles & Pros', 2), -0.04720
('Familles & Pros', 2), 0.04702

('Seniors actifs', 0), -0.36578
('Seniors actifs', 0), 0.00000

('Seniors actifs', 1), 0.03303
('Seniors actifs', 1), 0.13539

('Seniors actifs', 2), 0.00544
('Seniors actifs', 2), 0.80583

('Retraités', 0), -0.02935
('Retraités', 0), 0.27733

('Retraités', 1), -0.02213
('Retraités', 1), 0.41275

('Retraités', 2), -0.00948
('Retraités', 2), 0.72577

In [ ]: